#include "mainwindow.h"
#include "include/Pipeline.h"

#include "serialworker.h" // 包含SerialWorker头文件
#include "ui_mainwindow.h"
#include <QBuffer>
#include <QDateTime>
#include <QDebug>
#include <QHBoxLayout>
#include <QLabel>
#include <QProcess>
#include <QPropertyAnimation>
#include <QSerialPort>
#include <QThread> // 添加线程支持
#include <QTimer>
#include <QUdpSocket>
#include <fcntl.h>
#include <iostream>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
using namespace std;

QTimer *ti;
// 摄像头
cv::VideoCapture *video;
// 一帧数据
cv::Mat frame;

// 车牌识别对象
pr::PipelinePR *prc;

// 网络传输相关
QUdpSocket *udpSocket;
QUdpSocket *udpSocke_receive;
QHostAddress serverAddress;
quint16 serverPort;

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow) {
  ui->setupUi(this);

  // 创建主窗口

  // MainWindow::SmoothScrollingLabel();

  udpSocket = new QUdpSocket(this);        // 发送
  udpSocke_receive = new QUdpSocket(this); // 接收
                                           // 开发板1视频流
  udpSocke_receive->bind(5001);
  serverAddress = QHostAddress("192.168.7.174"); // 替换为电脑的实际IP

  serverPort = 5001;     // 第一个板使用5000，第二个使用5001
  ti = new QTimer(this); // 创建一个定时器
  connect(ti, &QTimer::timeout, this, &MainWindow::show_video);

  video = new cv::VideoCapture("/dev/video7"); // 新建一个摄像头

  video->open("/dev/video7"); // 打开摄像头
  if (!video->isOpened()) {
    qDebug() << "failed To Open Camera";
  }

  ti->start(100); // 开启定时器 100 表示定时器间隔为 100 毫秒

  // 新建车牌识别对象
  prc = new pr::PipelinePR(
      "model/cascade.xml", "model/HorizonalFinemapping.prototxt",
      "model/HorizonalFinemapping.caffemodel", "model/Segmentation.prototxt",
      "model/Segmentation.caffemodel", "model/CharacterRecognization.prototxt",
      "model/CharacterRecognization.caffemodel",
      "model/SegmenationFree-Inception.prototxt",
      "model/SegmenationFree-Inception.caffemodel");

  // 初始化串口线程
  serialThread = new QThread(this);  // 创建线程对象，指定父对象以便自动管理
  serialWorker = new SerialWorker(); // 创建串口工作对象

  // 将串口工作对象移动到线程中（重要：这是多线程的关键）
  serialWorker->moveToThread(serialThread);

  // 连接信号和槽
  connect(serialThread, &QThread::started, serialWorker,
          &SerialWorker::startReading); // 线程启动时开始读取
  connect(serialThread, &QThread::finished, serialWorker,
          &QObject::deleteLater); // 线程结束时删除工作对象
  connect(serialWorker, &SerialWorker::rfidCardDetected, this,
          &MainWindow::onRfidCardDetected); // RFID卡检测
  connect(serialWorker, &SerialWorker::errorOccurred, this,
          &MainWindow::onSerialError); // 错误处理

  // 启动线程
  serialThread->start(); // 启动串口工作线程

  connect(this, &MainWindow::recognize_car, this,
          &MainWindow::recognizeLicensePlate); // 触发检测车牌槽函数

  connect(udpSocke_receive, &QUdpSocket::readyRead, this,
          &MainWindow::receiver_licensePlate);
}

MainWindow::~MainWindow() {
  // 停止并退出线程
  if (serialThread && serialThread->isRunning()) {
    serialThread->quit(); // 请求线程退出
    serialThread->wait(); // 等待线程完全退出
  }

  delete ui;
  delete udpSocket;
}

void MainWindow::recognizeLicensePlate(
    QString cardNumber) // 执行这个槽函数识别车牌并将车牌号的数据发送到上位机
{
  qDebug() << "start recognizeLicensePlate ....";

  ti->stop();

  std::vector<pr::PlateInfo>
      res; // pr::PlateInfo 是 HyperLPR 库定义的车牌信息结构体
  // 一个图像中可能包含多个车牌，所以返回的是数组而不是单个对象

  // 尝试识别图片中的车牌信息
  // 若图片中不含车牌信息，接口会抛出异常
  try {
    res = prc->RunPiplineAsImage(frame, pr::SEGMENTATION_FREE_METHOD);
    // 函数内部实现出错会抛出异常
  } catch (...) {
    qDebug() << "Not Detected  number" << endl;
    // 重新开启定时器
    ti->start(100);

    return;
  }
  if (flag == 1) {
    // 字符串
    // string pn;
    for (auto st : res) {
      // 获取车牌

      pn = st.getPlateName();
      if (pn.length() >= 9) {

        qDebug() << "DetectedLicensePlate: " << pn.data();
        qDebug() << "ConfidenceRate: "
                 << st.confidence; // 置信度：表示识别结果的可靠程度
        confidence = st.confidence;
        // ui->label_2->setText(QString(pn.data()));

        // 可以将识别结果也发送给上位机
        QByteArray plateData;
        plateData.append(pn.c_str()); // 返回 C 风格的字符串指针
        //  PLATE:京A12345 最后的数据实例文件头用来接收端检测
        qDebug() << plateData;

        // 获取当前本地时间
        QDateTime currentDateTime = QDateTime::currentDateTime();

        // 转换为字符串显示
        QString dateTimeString =
            currentDateTime.toString("yyyy年MM月dd日 HH:mm:ss");
        qDebug() << "--------------------cur_time:" << dateTimeString;
        // 11111、
        plateData.append("," + cardNumber);
        // plateData.append("," + byteArray);
        plateData.append("," + dateTimeString);
        plateData.append("," + QString::number(st.confidence));

        udpSocket->writeDatagram(plateData, serverAddress,
                                 serverPort + 10); // 使用不同的端口发送识别结果
        flag = 0;
        break;
      }
    }
  }
  // 等待接收数据进行语音播报

  // mainWindow->receiver_licensePlate(); // 接收信息将要播报的text获取到

  // 启动定时器，5秒后返回广告页
  // pageManager->returnTimer->start(pageManager->returnDelay);
  // 重新开启定时器
  ti->start(100);
}

// 显示摄像头采集的画面并发送到上位机
void MainWindow::show_video() {
  // 采集数据帧
  video->read(frame); // 读取数据帧

  if (frame.empty()) {
    return;
  }

  // 发送视频帧到上位机
  send_frame_to_server(frame);

  // 显示数据帧
  QImage img = QImage((const unsigned char *)(frame.data), frame.cols,
                      frame.rows, frame.step, QImage::Format_RGB888)
                   .rgbSwapped();

  // 将QImage显示在QLabel中
  // ui->label->setPixmap(QPixmap::fromImage(img));
}

// 这个函数负责将OpenCV图像帧转换为QImage，压缩为JPEG格式，然后通过UDP发送到服务器。

void MainWindow::send_frame_to_server(cv::Mat &frame) {
  // 将OpenCV Mat转换为QImage
  cv::Mat rgbFrame; // 创建一帧数据
  cv::cvtColor(frame, rgbFrame,
               cv::COLOR_BGR2RGB); //
  // OpenCV：早期基于Intel的图像处理库，采用BGR顺序这里将BGR转为rgb
  QImage img((const uchar *)rgbFrame.data, rgbFrame.cols, rgbFrame.rows,
             rgbFrame.step, QImage::Format_RGB888);
  // rgbFrame.data：图像数据指针
  // rgbFrame.cols：图像宽度（列数）
  // rgbFrame.rows：图像高度（行数）
  // rgbFrame.step：每行字节数（步长）
  // QImage::Format_RGB888：图像格式（24位RGB）
  // 压缩图像为JPEG格式

  QBuffer buffer(
      &byteArray); // QBuffer是内存中的I/O设备//img.save()需要QIODevice参数
  buffer.open(QIODevice::WriteOnly); // 以只写模式打开缓冲区
  img.save(&buffer, "JPEG",
           50); // 50%质量以减小数据量 将图像以JPEG格式保存到缓冲区，质量为50%

  // 通过UDP发送
  udpSocket->writeDatagram(byteArray, serverAddress, serverPort);

  //  // 可选：添加帧计数器或时间戳
  //  static int frameCount = 0;
  //  frameCount++;
  //  if (frameCount % 30 == 0) { //
  //  每30帧发送一次状态信息，30帧率差不多多久
  //    QByteArray status;
  //    status.append("STATUS:Frame:");
  //    status.append(QString::number(frameCount));
  //    udpSocket->writeDatagram(status, serverAddress,
  //                             serverPort + 20); //
  //  }
}

// QString change_plane(QString plane) {
//   if (plane.isEmpty()) {
//     return nullptr;
//   }

//  QString result;

//  // 处理第一个字符（省份简称）
//  if (plane.length() > 0) {
//    result += plane[0];
//  }

//  // 处理剩余字符
//  for (int i = 1; i < plane.length(); i++) {
//    QChar ch = plane[i];
//    result += " "; // 在每个字符前添加空格

//    if (ch.isDigit()) {
//      // 数字转换为中文读法
//      switch (ch.digitValue()) {
//      case 0:
//        result += "零";
//        break;
//      case 1:
//        result += "幺";
//        break; // 数字1特殊处理为"幺"
//      case 2:
//        result += "二";
//        break;
//      case 3:
//        result += "三";
//        break;
//      case 4:
//        result += "四";
//        break;
//      case 5:
//        result += "五";
//        break;
//      case 6:
//        result += "六";
//        break;
//      case 7:
//        result += "七";
//        break;
//      case 8:
//        result += "八";
//        break;
//      case 9:
//        result += "九";
//        break;
//      default:
//        break;
//      }
//    } else if (ch.isLetter()) {
//      // 字母保持原样
//      result += ch;
//    } else {
//      // 其他字符（如中文）保持原样
//      result += ch;
//    }
//  }

//  // 这里可以返回结果或者进行其他操作
//  qDebug() << "befpre change:" << plane;
//  qDebug() << "after change:" << result;

//  return result;
//}

void MainWindow::SmoothScrollingLabel(QString text) // 平滑滚动
{
  qDebug() << text;

  QString plane = text.section(",", 0, 0); // 车牌
  QString money = text.section(",", 1, 1); // 钱
  QString output("");
  // 出 开发板
  output.append("车牌" + plane + "消费" + money + "元");
  //    入  车牌京A12345，已入场。祝您停车愉快！
  // output.append("车牌" + plane + "已入场。祝您停车愉快");
  QPropertyAnimation *animation;
  ui->label_scroll->setText(output);

  ui->label_scroll->setFixedWidth(
      ui->label_scroll->sizeHint().width()); // 根据文本设置宽度
  animation = new QPropertyAnimation(ui->label_scroll, "pos");
  animation->setDuration(8000); // 动 画持续时间
  animation->setStartValue(
      QPoint(ui->label_scroll->width(), 300)); // 文字起始位置
  animation->setEndValue(
      QPoint(-ui->label_scroll->width(), 300)); // 文字zhong'dian位置

  animation->setLoopCount(-1);

  animation->start(); // 无限循环
                      // output = change_plane(output);
  qDebug() << "yu yin----------" << output;
  // QProcess *process = new QProcess(this);
  // process->start("espeak", QStringList() << "-v" << "en" << output);
  qDebug() << "**************************_________________________" << output;
  system(QString("espeak -v zh '%1' ").arg(output).toLocal8Bit().constData());
  //  system(QString("espeak -v zh %1")
  //             .arg(output)
  //             .toLocal8Bit()
  //             .constData()); // 语音播报
  //                            // 开启计时器

  qDebug() << "***************";
}

void MainWindow::onRfidCardDetected(QString cardNumber) {
  qDebug() << "zhu xian cheng jie shou dao RFID id:" << cardNumber;

  // 通知页面管理器显示主页面

  // 这里需要通过信号通知页面管理器，或者直接调用方法
  emit rfidCardDetected(cardNumber); // 刷卡就发送信号进行切换
  emit recognize_car(cardNumber);
  emit open_return_timer(); // 开启广告定时器
  qDebug() << "yi fa she rfidCardDetected signal";
}

void MainWindow::onSerialError(QString error) {
  qDebug() << "chuankou error:" << error;
  // 可以在这里添加错误处理逻辑，比如显示错误消息
}
void MainWindow::receiver_licensePlate() {
  // 判断是否有数据  这里一直跑因为
  //  这个while循环只处理已经到达的数据
  //  QT的循环事件的机制：数据到达 → 操作系统通知 → QT事件循环 →
  //  触发readyRead信号 → 调用您的槽函数
  while (udpSocke_receive->hasPendingDatagrams()) {
    // 1. 获取数据包大小并准备缓冲区
    QByteArray datagram;
    datagram.resize(
        udpSocke_receive
            ->pendingDatagramSize()); // 设置这个数组的空间大小和数据包的大小一志
    // 2. 读取数据包
    QHostAddress sender; // ip
    quint16 senderPort;  // 端口
    udpSocke_receive->readDatagram(datagram.data(), datagram.size(), &sender,
                                   &senderPort);
    text = QString::fromUtf8(datagram);
    qDebug() << "1111——---------------- jieshou xinxi " << text;
  }
  // 识别出来了就滚动
  SmoothScrollingLabel(text); // 滚动text加上播报
  // 播报结束后就打开计时器，因为计时器在Pagemanager里面所以发信号开启定时器

  // qDebug() << "1"; //这里会一直有1输出  因为这个函数会结束
}

void MainWindow::clear_label_text() // 清空标签
{
  flag = 1;
  // 1. 停止所有动画
  QList<QPropertyAnimation *> animations = findChildren<QPropertyAnimation *>();
  foreach (QPropertyAnimation *anim, animations) {
    anim->stop();
    anim->deleteLater();
  }

  // 2. 清空文本
  ui->label_scroll->setText("");

  // 3. 强制立即重绘（关键步骤！）
  ui->label_scroll->repaint(); // 立即重绘
}
